로딩 중이에요... 🐣
[코담]
웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트
12 포스트 등록 화면 구성 (장고 Form 활용) | ✅ 편저: 코담 운영자
12강 - 포스트 등록 화면 구성 (장고 Form 활용)
등록 화면 - form✨ 이번 강의 목표
- 장고 Form 클래스를 활용해 사용자 게시물 등록 처리
- 폼 유효성 검사 및 템플릿 연동 처리
- form.as_p 렌더링과 템플릿 block 구조 이해
1. 폼 클래스 정의 (CreatePostForm)
posts/forms.py
from django import forms
from .models import Post
class CreatePostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['caption', 'image']
labels = {
'caption': '내용',
'image': '사진'
}
def clean_caption(self):
caption = self.cleaned_data.get("caption")
if len(caption) < 5:
raise forms.ValidationError("내용은 최소 5자 이상이어야 합니다.")
return caption
def clean_image(self):
image = self.cleaned_data.get("image")
if image is None:
raise forms.ValidationError("이미지를 업로드해야 합니다.")
if not image.name.lower().endswith((".png", ".jpg", ".jpeg")):
raise forms.ValidationError("지원하는 이미지 형식은 PNG, JPG, JPEG입니다.")
return image
✅ 설명
ModelForm
을 상속받아 모델 기반 폼 구성Meta
클래스를 통해 대상 모델과 필드 설정labels
로 필드 라벨명 커스터마이징 가능
2. 뷰에서 폼 처리
posts/views.py
from .forms import CreatePostForm
# GET 요청 시: 폼을 생성하여 템플릿으로 전달
# POST 요청 시: 폼에 데이터 바인딩 후 유효성 검사 및 저장
def post_create(request):
if request.method == 'POST':
form = CreatePostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return render(request, 'posts/main.html')
else:
form = CreatePostForm()
return render(request, 'posts/post_create.html', {'form': form})
else:
return render(request, 'users/main.html', {'error_message': '권한오류: post 등록에 실패하였습니다.'})
✅ 설명
commit=False
는 저장을 지연하고 추가 데이터를 넣기 위해 사용됨 (작성자 설정 등)request.FILES
는 이미지 업로드를 위한 데이터 처리용
3. 템플릿에서 폼 렌더링 (직접 필드 렌더링 방식)
templates/posts/post_create.html
{% extends 'posts/base.html' %}
{% block title %}포스트 생성{% endblock %}
{% block content %}
<div class="w-full flex justify-center items-center min-h-screen bg-gray-100">
<div class="w-2/3 bg-white rounded-lg p-8 shadow-md">
<h2 class="text-xl font-bold text-center mb-4">포스트 등록</h2>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
<label for="id_caption" class="block text-sm font-medium text-gray-700 mb-1">내용</label>
<textarea id="id_caption" name="caption" class="w-full border-gray-300 rounded-lg shadow-sm focus:border-indigo-500 focus:ring-indigo-500" rows="12" required></textarea>
{% if form.caption.errors %}
<p class="text-sm text-red-500 mt-1">{{ form.caption.errors.0 }}</p>
{% endif %}
</div>
<div>
<label for="id_image" class="block text-sm font-medium text-gray-700 mb-1">사진</label>
<input required id="id_image" type="file" name="image" class="w-full file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:bg-indigo-600 file:text-white file:font-medium file:cursor-pointer file:hover:bg-indigo-500" />
{% if form.image.errors %}
<p class="text-sm text-red-500 mt-1">{{ form.image.errors.0 }}</p>
{% endif %}
</div>
<button type="submit" class="mt-4 w-full py-2 bg-blue-600 text-white font-semibold rounded">등록하기</button>
</form>
</div>
</div>
{% endblock %}
✅ 설명
form.as_p
는 각 필드를<p>
태그로 감싸 렌더링enctype="multipart/form-data"
는 이미지 업로드 시 필수 설정csrf_token
은 보안상 필수 입력
4. 유효성 검사와 required 처리
문제 상황:
사용자가 내용 또는 이미지를 비워도 저장되는 문제
해결 방법:
- 모델의
blank=False
설정 필요:
caption = models.TextField(blank=False)
image = models.ImageField(upload_to='posts/', blank=False)
blank=False
: 폼에서 빈 입력 허용하지 않음 (required 역할)null=False
: DB에서 반드시 값이 있어야 함
→ 두 필드 모두 필수 입력값으로 처리됨
✅ 정리
ModelForm
을 활용하면 입력 필드 선언 없이 모델 기반 폼 생성 가능- 뷰에서는
form.is_valid()
를 통해 유효성 검사를 거쳐 저장 처리 - 템플릿에서 각 필드를 직접 구성하면 Tailwind 스타일을 세밀하게 적용할 수 있음
blank=False
설정으로 필수 입력값 제한 가능
👉 다음 강의에서는 메인 피드(index.html)에서 등록된 게시글 리스트를 출력합니다.